package org.vison.wonfu.framework.soa.context;

import org.vison.wonfu.framework.soa.Invocation;
import org.vison.wonfu.framework.soa.Invoker;

import java.net.InetSocketAddress;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceContext {
    private List<URL> urls;

    private URL url;

    private Invocation invocation;

    private String remoteApplication;
    private String commandKey;
    private String serviceName;
    private String methodName;
    private Class<?>[] parameterTypes;
    private Object[] arguments;

    private InetSocketAddress remoteAddress;

    private final ConcurrentMap<String, Object> attachments = new ConcurrentHashMap<>();

    public void setInvokers(List<Invoker<?>> invokers) {

    }

    public void setInvoker(Invoker<?> invoker) {

    }

    public void setInvocation(Invocation invocation) {
        if (invocation != null) {
            this.invocation = invocation;
            setRemoteApplication(invocation.getRemoteApplication());
            setCommandKey(invocation.getCommandKey());
            setServiceName(invocation.getServiceName());
            setMethodName(invocation.getMethodName());
            setParameterTypes(invocation.getParameterTypes());
            setArguments(invocation.getArguments());
        }
    }

    /**
     * get Invocation
     *
     * @return maybe return null
     */
    public Invocation getInvocation() {
        return invocation;
    }

    /**
     * is provider side.
     *
     * @return provider side.
     */
    public boolean isProviderSide() {
        return !isConsumerSide();
    }

    /**
     * is consumer side.
     *
     * @return consumer side.
     */
    public boolean isConsumerSide() {
        return false;
    }

    public List<URL> getUrls() {
        return urls == null && url != null ? Collections.singletonList(url) : urls;
    }

    public void setUrls(List<URL> urls) {
        this.urls = urls;
    }

    public URL getUrl() {
        return url;
    }

    public void setUrl(URL url) {
        this.url = url;
    }

    public String getRemoteApplication() {
        return remoteApplication;
    }

    public void setRemoteApplication(String remoteApplication) {
        this.remoteApplication = remoteApplication;
    }

    public String getCommandKey() {
        return commandKey;
    }

    public void setCommandKey(String commandKey) {
        this.commandKey = commandKey;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class<?>[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    public Object[] getArguments() {
        return arguments;
    }

    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }

    public void setRemoteAddress(String host, int port) {
        if (port < 0) {
            port = 0;
        }
        this.remoteAddress = InetSocketAddress.createUnresolved(host, port);
    }

    public void setRemoteAddress(InetSocketAddress address) {
        this.remoteAddress = address;
    }

    public InetSocketAddress getRemoteAddress() {
        return remoteAddress;
    }

    public String getRemoteAddressString() {
        return getRemoteHost() + ":" + getRemotePort();
    }

    public String getRemoteHostName() {
        return remoteAddress == null ? null : remoteAddress.getHostName();
    }

    public String getRemoteHost() {
       return remoteAddress == null ? null : remoteAddress.getHostString();
    }

    public int getRemotePort() {
        return remoteAddress == null ? 0 : remoteAddress.getPort();
    }

    public ConcurrentMap<String, Object> getAttachments() {
        return attachments;
    }

    public void putAttachment(String key, Object value) {
        attachments.put(key, value);
    }

    public void putAllAttachments(Map<String, Object> map) {
        if (map == null) {
            return;
        }
        this.attachments.putAll(map);
    }

    public Object getAttachment(String key) {
        return attachments.get(key);
    }

    public Object getAttachment(String key, Object defaultValue) {
        return attachments.getOrDefault(key, defaultValue);
    }

    public String getAttachmentString(String key) {
        return getAttachmentString(key, null);
    }

    public String getAttachmentString(String key, String defaultValue) {
        return Optional.of(attachments)
                .map(a -> a.get(key))
                .filter(v -> v instanceof String)
                .map(Object::toString)
                .orElse(defaultValue);
    }

    /**
     * deep copy
     **/
    public ServiceContext copy() {
        ServiceContext copy = new ServiceContext();

        copy.urls = this.urls;
        copy.url = this.url;
        copy.invocation = this.invocation;
        copy.remoteApplication = this.remoteApplication;
        copy.commandKey = this.commandKey;
        copy.serviceName = this.serviceName;
        copy.methodName = this.methodName;
        copy.parameterTypes = this.parameterTypes;
        copy.arguments = this.arguments;
        copy.remoteAddress = this.remoteAddress;

        // deep copy
        if (!this.attachments.isEmpty()) {
            copy.attachments.putAll(this.attachments);
        }

        return copy;
    }
}
